package com.kun.handler.datapermission;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.kun.entity.SysRole;
import com.kun.entity.SysUser;
import com.kun.enums.DataScopeEnums;
import com.kun.note.DataScope;
import com.kun.remote.RemoteRoleService;
import com.kun.remote.RemoteUserService;
import com.kun.util.LoginUserContext;
import com.kun.util.SpringContextHolder;
import lombok.Data;
import lombok.SneakyThrows;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据权限实现
 *
 * @author kun.li
 */
public class DefaultDataPermissionHandler implements DataPermissionHandler {

    private final Map<String, Item> cacheMap = new HashMap<>();

    @SneakyThrows
    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        Item item = hasDataScope(mappedStatementId);
        // 有权限
        if (item.isHasAuthSecurity()) {
            Expression expression = getExpression(item);
            if (where != null) {
                return new AndExpression(where, expression);
            }
            return expression;
        }
        return where;
    }

    private static Expression getExpression(Item item) {
        SysUser sysUser = LoginUserContext.getUser();
        String userId = sysUser.getUserId();
        RemoteRoleService remoteRoleService = SpringContextHolder.getBean(RemoteRoleService.class);
        List<SysRole> roles = remoteRoleService.getRoleList(userId);
        // 多角色
        List<SysRole> sysRoles = roles.stream().filter(role -> role.getDsType() == 0).toList();
        Expression expression;
        // 只要有一个角色拥有全部权限就取最高权限
        if (CollUtil.isNotEmpty(sysRoles)) {
            expression = new HexValue("1=1");
        } else {
            RemoteUserService remoteUserService = SpringContextHolder.getBean(RemoteUserService.class);
            Set<String> conditions = new HashSet<>();
            for (SysRole sysRole : roles) {
                Integer dsType = sysRole.getDsType();
                String dsScope = sysRole.getDsScope();
                if (Objects.equals(DataScopeEnums.SELF.getDataScopeType(), dsType)) {
                    conditions.add(sysUser.getUsername());
                    continue;
                }
                List<SysUser> sysUsers = remoteUserService.selectUserByDeptId(dsScope);
                List<String> userNames = sysUsers.stream().map(SysUser::getUsername).toList();
                // 除去全部权限 和本人权限
                if (DataScopeEnums.isDataScope(dsType)) {
                    conditions.addAll(userNames);
                }
            }
            ExpressionList expressionList = new ExpressionList(conditions.stream().map(StringValue::new).collect(Collectors.toList()));
            expression = new InExpression(new Column().withColumnName(item.getColumn()), expressionList);
        }
        return expression;
    }

    private Item hasDataScope(String mappedStatementId) throws ClassNotFoundException {
        if (cacheMap.get(mappedStatementId) == null) {
            Item item = new Item();
            String className = mappedStatementId.substring(0, mappedStatementId.lastIndexOf("."));
            String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);
            Class<?> aClass = Class.forName(className);
            Method[] methods = aClass.getMethods();
            if (aClass.isAnnotationPresent(DataScope.class)) {
                DataScope dataScope = aClass.getAnnotation(DataScope.class);
                String[] dataScopeMethod = dataScope.methods();
                item.setColumn(dataScope.value());
                List<String> methodList = new ArrayList<>(Arrays.asList(dataScopeMethod));
                if (methodList.contains(methodName)) {
                    item.setHasAuthSecurity(true);
                }
            }
            for (Method method : methods) {
                String name = method.getName();
                if (name.equals(methodName) && method.isAnnotationPresent(DataScope.class)) {
                    item.setHasAuthSecurity(true);
                    DataScope dataScope = method.getAnnotation(DataScope.class);
                    item.setColumn(dataScope.value());
                    break;
                }
            }
            cacheMap.put(mappedStatementId, item);
            return item;
        }
        return cacheMap.get(mappedStatementId);
    }


    @Data
    private static class Item {

        private boolean hasAuthSecurity;

        private String column;
    }


}
